//
// Created by cjh on 2021/5/10.
//

#include "asocket.h"
#include <iostream>
#include <string>
#include "loop.h"

using namespace std;

asocket::asocket(int family, int type, int protocol)
    : asocket(::socket(family, type, protocol)){}

asocket::asocket(class socket &&s)
    : sock(std::forward<class socket>(s)), async_io(&sock)
{
    event_mask = EPOLLERR|EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLPRI|EPOLLET;
    if(sock.valid()) sock.set_block(false);
}

asocket::~asocket()
{}

void asocket::on_error() {
    std::string err("asocket::on_error EPOLLERR happened: ");
    err += strerror(errno);
    err += ", you can override on_error() to handle it.";
    throw std::runtime_error(err);
}

void asocket::on_write() {
//    std::string  err("asocket::on_write EPOLLOUT happened: ");
//    err += "you can override on_write() to stop this error";
//    throw std::runtime_error(err);
    if(write.waiting()) {
        coro.resume();
    }
}

void asocket::on_read() {
    if(read.waiting()) {
        coro.resume();
    }
}

void asocket::on_close() {
//    std::string  err("asocket::on_close EPOLLRDHUP happened: ");
//    err += "you can override on_close() to stop this error.";
//    throw std::runtime_error(err);
    if(!_closing) {
        _closing = true;
        loop->set_timeout(0, [this]() {
            closing();
            while(read.waiting() || write.waiting()) {
                read.stop(), write.stop();
                coro.resume();
            }
            coro.reset();
            loop->rmv_socket(fileno());
            loop = nullptr;
            sock.close();
        });
    }
}

void asocket::open(class socket &&s, event_loop *l) {
    //  check
    if(sock.valid() || loop) {
        throw std::runtime_error("asocket::open() error: current socket is not close.");
    }

    //  assign socket
    sock = std::forward<class socket>(s);
    sock.set_block(false);
    aio_init(&sock);

    //  initialize variable
    _closing = false;
    if(l)
        l->add_socket(this);
}
